﻿
namespace TestApp.Util
{
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Text.RegularExpressions;

    public class MatrixCollection
    {
        List<MatrixInfo> items = new List<MatrixInfo>();

        public List<MatrixInfo> Items
        {
            get { return items; }
        }

        public static MatrixCollection LoadDefault()
        {
            var c = new MatrixCollection();

            c.Add(MatrixInfo.Create("FIDAP", "ex2", 441, 441, 13640, "real", true));
            c.Add(MatrixInfo.Create("FIDAP", "ex10", 2410, 2410, 54840, "real", true, true));
            c.Add(MatrixInfo.Create("FIDAP", "ex11", 16614, 16614, 1096948, "real", false));
            c.Add(MatrixInfo.Create("TOKAMAK", "utm5940", 5940, 5940, 83842, "real", false));
            c.Add(MatrixInfo.Create("HB", "saylr3", 1000, 1000, 2375, "real", true));
            c.Add(MatrixInfo.Create("HB", "saylr4", 3564, 3564, 22316, "real", false));
            c.Add(MatrixInfo.Create("HB", "sherman1", 1000, 1000, 3750, "real", false));
            c.Add(MatrixInfo.Create("HB", "young1c", 841, 841, 4089, "complex", false));
            c.Add(MatrixInfo.Create("HB", "well1850", 1850, 712, 8755, "real", false));
            c.Add(MatrixInfo.Create("HB", "illc1850", 1850, 712, 8636, "real", false));
            c.Add(MatrixInfo.Create("YCheng", "psse0", 26722, 11028, 102432, "real", false));
            c.Add(MatrixInfo.Create("YCheng", "psse2", 28634, 11028, 115262, "real", false));

            return c;
        }

        public void ToCsv(string path)
        {
            using (var writer = new StreamWriter(path))
            {
                foreach (var item in items)
                {
                    writer.Write("{0};", item.Group);
                    writer.Write("{0};", item.Name);
                    //writer.Write("", item.Id);
                    writer.Write("{0};", item.Rows);
                    writer.Write("{0};", item.Cols);
                    writer.Write("{0};", item.NonZeros);
                    writer.Write("{0};", item.Type);
                    writer.Write("{0};", item.IsSymmetric ? 1 : 0);
                    writer.Write("{0}", item.IsPositiveDefinit ? 1 : 0);
                    writer.WriteLine();
                }
            }
        }

        private void Add(MatrixInfo matrixInfo)
        {
            items.Add(matrixInfo);
        }

        Dictionary<string, int> types = new Dictionary<string, int>();
        Dictionary<string, int> syms = new Dictionary<string, int>();
        Dictionary<string, int> spds = new Dictionary<string, int>();

        public void Parse(StreamReader stream)
        {
            ParseHtml(stream);
        }

        /// <summary>
        /// Parse HTML from UF Sparse Matrix Collection.
        /// </summary>
        /// <param name="stream"></param>
        /// <remarks>
        /// http://www.cise.ufl.edu/research/sparse/matrices/list_by_type.html
        /// </remarks>
        private void ParseHtml(StreamReader stream)
        {
            while (!stream.EndOfStream)
            {
                if (!SkipToNextMatrix(stream))
                {
                    break;
                }

                items.Add(ParseMatrix(stream));
            }
        }

        private MatrixInfo ParseMatrix(StreamReader stream)
        {
            for (int i = 0; i < 4; i++)
            {
                stream.ReadLine();
            }

            string group, name;
            GetGroupAndName(stream.ReadLine(), out group, out name);

            var id = GetTagContent(stream.ReadLine());

            stream.ReadLine();
            stream.ReadLine();

            var rows = GetTagContent(stream.ReadLine()).Replace(",", String.Empty);
            var cols = GetTagContent(stream.ReadLine()).Replace(",", String.Empty);
            var nonzeros = GetTagContent(stream.ReadLine()).Replace(",", String.Empty);
            var type = GetTagContent(stream.ReadLine());
            var sym = GetTagContent(stream.ReadLine());
            var spd = GetTagContent(stream.ReadLine());

            if (!types.ContainsKey(type)) types.Add(type, 1); else types[type]++;
            if (!syms.ContainsKey(sym)) syms.Add(sym, 1); else syms[sym]++;
            if (!spds.ContainsKey(spd)) spds.Add(spd, 1); else spds[spd]++;

            return MatrixInfo.Create(group, name, id, rows, cols, nonzeros, type, sym, spd);
        }

        private void GetGroupAndName(string line, out string group, out string name)
        {
            line = Regex.Replace(line, @"<[^>]+>", string.Empty);

            var s = line.Split('/');

            group = s[0];
            name = s[1];
        }

        private string GetTagContent(string line)
        {
            int i = line.IndexOf('>');
            int j = line.IndexOf('<', i + 1);

            return line.Substring(i + 1, j - i - 1).Trim();
        }

        private bool SkipToNextMatrix(StreamReader stream)
        {
            string line = null;

            while (!stream.EndOfStream)
            {
                line = stream.ReadLine().Trim();

                if (line.Equals("<tr>", StringComparison.OrdinalIgnoreCase))
                {
                    line = stream.ReadLine().Trim();

                    if (line.StartsWith("<td>", StringComparison.OrdinalIgnoreCase))
                    {
                        return true;
                    }
                }
            }

            return false;
        }
    }
}
